package session.stateful;

import entity.AddressEntity;
import entity.BranchEntity;
import entity.FnBBranchEntity;
import entity.FnBIOTREntity;
import entity.PackagePurchasedEntity;
import entity.PackagePurchasedPaymentItemEntity;
import entity.ServiceEntity;
import entity.ServicePackageEntity;
import entity.StakeholderEntity;
import java.util.Date;
import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import exception.ChangeServicePackageException;
import exception.ExtendServicePackageException;
import exception.TerminateServicePackageException;

@Stateful
@LocalBean
public class BranchSessionBeanStateful {

    private static String TRIAL = "Trial Package";
    private static String FREE = "Free Package";
    private static int MONTHOFTRIAL = 1;
    private static double changeFee = 5;
    private static double cancelFee = 5;
    @PersistenceContext
    private EntityManager entityManager;

    public List<BranchEntity> getAllBranchByStakeholderId(Long stakeholderId) {
        StakeholderEntity stakeholder = entityManager.find(StakeholderEntity.class, stakeholderId);
        return stakeholder.getBranches();
    }

    public List<BranchEntity> getAllBranch() {
        Query query = entityManager.createQuery("SELECT b FROM BranchEntity b");
        return query.getResultList();
    }

    public void editBranchInfo(FnBBranchEntity branch) {
        entityManager.merge(branch.getFnBIOTR());
        entityManager.merge(branch);
    }

    private Date getNextExpiredDate(int billingDay, Date date) {
        if (billingDay <= date.getDate()) {
            Date tempDate = new Date(date.getYear(), date.getMonth(), billingDay);
            return getExpiredDate(tempDate, 1);
        }

        int maxDay = getMaxDayOfMonth(date.getMonth(), date.getYear());
        Date tempDate = new Date(date.getYear(), date.getMonth(), Math.min(billingDay, maxDay));
        return tempDate;
    }

    private int getMaxDayOfMonth(int month, int year) {
        switch (month) {
            case 0:
            case 2:
            case 4:
            case 6:
            case 7:
            case 9:
            case 11:
                return 31;
            case 1:
                if (isLeapYear(year)) {
                    return 29;
                } else {
                    return 28;
                }
            default:
                return 30;
        }
    }

    public void buyNewPackage(long branchId, Date datePurchased, long selectedServicePackageId, int numOfMonth) throws ChangeServicePackageException {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);
        ServicePackageEntity selectedServicePackage = entityManager.find(ServicePackageEntity.class, selectedServicePackageId);

        if (!selectedServicePackage.getName().equalsIgnoreCase(TRIAL) && !selectedServicePackage.getName().equalsIgnoreCase(FREE)
                && !selectedServicePackage.isVisibility()) {
            throw new ChangeServicePackageException("This service package is not offering now! So you can't buy it");
        }

        PackagePurchasedEntity pack = new PackagePurchasedEntity();

        Date dateExpired = getExpiredDate(datePurchased, numOfMonth);

        pack.setPrice(selectedServicePackage.getPrice());
        pack.setDateExpired(dateExpired);
        pack.setDatePurchased(datePurchased);
        pack.setBranch(branch);
        pack.setServicePackage(selectedServicePackage);

        entityManager.persist(pack);
        selectedServicePackage.getPackagePurchased().add(pack);

        branch.getPackagePurchased().add(pack);
        System.out.println("I COME HERE");
        //automatically add payment item to this stakeholder if this one is not a free nor trial service package
        Date date = new Date();
        if (!selectedServicePackage.getName().equalsIgnoreCase(TRIAL) && !selectedServicePackage.getName().equalsIgnoreCase(FREE)
                && date.getYear() == datePurchased.getYear() && date.getMonth() == datePurchased.getMonth()
                && date.getDate() == datePurchased.getDate()) {
            ServicePackageEntity servicePack = pack.getServicePackage();
            StakeholderEntity stakeholder = branch.getStakeholder();
            PackagePurchasedPaymentItemEntity payment = new PackagePurchasedPaymentItemEntity();
            payment.setPackagePurchased(pack);
            payment.setAmountToTCube(pack.getPrice());
            String explanation = "Subscription fee for using package " + servicePack.getName()
                    + " in one month start from " + date.getDate() + "/" + (date.getMonth() + 1) + "/" + (date.getYear() + 1900);
            payment.setExplanation(explanation);
            payment.setDateCreated(date);
            payment.setStakeholder(stakeholder);
            //set payment item to payment management
            entityManager.persist(payment);
            stakeholder.getPaymentItem().add(payment);
            entityManager.merge(stakeholder);
        }
    }

    private int getCurrentPackage(List<PackagePurchasedEntity> tempList) {
        Date date = new Date();
        int pos = 0;
        //find the current package that this branch is using now
        for (; pos < tempList.size(); pos++) {
            if (tempList.get(pos).getDatePurchased().compareTo(date) <= 0
                    && tempList.get(pos).getDateExpired().compareTo(date) > 0) {
                return pos;
            }
        }
        return -1;
    }
    //Added by Khanh Vu 19/10/2011

    public List<ServiceEntity> getListOfServicesByBranchId(long branchId) {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);

        List<PackagePurchasedEntity> tempList = branch.getPackagePurchased();
        int pos = getCurrentPackage(tempList);

        if (pos != -1) {
            return tempList.get(pos).getServicePackage().getService();
        }

        return null;
    }
    //Added by Khanh Vu 19/10/2011

    private void deleteRedundantPackagePurchased(List<PackagePurchasedEntity> tempList) {
        for (int i = tempList.size() - 1; i >= 0; i--) {
            PackagePurchasedEntity p = tempList.get(i);
            if (p.getDatePurchased().compareTo(new Date()) > 0) {
                tempList.remove(p);
                p.getServicePackage().getPackagePurchased().remove(p);
                entityManager.merge(p.getServicePackage());
                entityManager.remove(p);
            }

        }
    }

    public void extendCurrentPackage(long branchId, int numOfMonth) throws ExtendServicePackageException {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);
        List<PackagePurchasedEntity> tempList = branch.getPackagePurchased();

        if (!tempList.isEmpty()) {
            int pos = getCurrentPackage(tempList);
            PackagePurchasedEntity currentPackage = new PackagePurchasedEntity();

            if (pos != - 1) {
                currentPackage = tempList.get(pos);
            } else {
                throw new ExtendServicePackageException("There is an error");
            }

            if (!currentPackage.getServicePackage().isVisibility()) {
                throw new ExtendServicePackageException("This service package is not offering now! So you can't extend it");
            }

            if (currentPackage.getServicePackage().getName().equalsIgnoreCase(FREE)) {
                throw new ExtendServicePackageException("You can't extend free package");
            }

            if (currentPackage.getServicePackage().getName().equalsIgnoreCase(TRIAL)) {
                throw new ExtendServicePackageException("You can't extend trial package");
            }

            deleteRedundantPackagePurchased(tempList);

            ServicePackageEntity sp = currentPackage.getServicePackage();
            if (currentPackage.getPrice() == sp.getPrice()) {
                currentPackage.setDateExpired(getExpiredDate(currentPackage.getDateExpired(), numOfMonth));
                entityManager.merge(currentPackage);
            } else {
                PackagePurchasedEntity newPackage = new PackagePurchasedEntity();
                newPackage.setServicePackage(sp);

                Date newDate = currentPackage.getDateExpired();

                newPackage.setDatePurchased(newDate);
                newPackage.setDateExpired(getExpiredDate(newDate, numOfMonth));
                newPackage.setPrice(sp.getPrice());

                entityManager.persist(newPackage);
                tempList.add(newPackage);
            }
        } else {
            throw new ExtendServicePackageException("You don't have any valid service package yet.");
        }
        entityManager.merge(branch);
    }

    public void terminateCurrentPackage(long branchId, boolean terminate) throws TerminateServicePackageException {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);
        List<PackagePurchasedEntity> tempList = branch.getPackagePurchased();

        if (!tempList.isEmpty()) {
            int pos = getCurrentPackage(tempList);
            PackagePurchasedEntity currentPackage = new PackagePurchasedEntity();

            if (pos != - 1) {
                currentPackage = tempList.get(pos);
            } else {
                throw new TerminateServicePackageException("There is an error");
            }

            if (currentPackage.getServicePackage().getName().equalsIgnoreCase(FREE)) {
                throw new TerminateServicePackageException("You can't terminate free package");
            }

            if (currentPackage.getServicePackage().getName().equalsIgnoreCase(TRIAL)) {
                throw new TerminateServicePackageException("You can't terminate trial package");
            }
            deleteRedundantPackagePurchased(tempList);

            Date date = new Date();
            int billingDay = branch.getBillingDay();

            int newDate = Math.min(billingDay, getMaxDayOfMonth(date.getMonth(), date.getYear()));
            Date newExpiredDate = new Date(date.getYear(), date.getMonth(), newDate);
            if (date.getDate() >= billingDay) {
                newExpiredDate = getExpiredDate(newExpiredDate, 1);
            }

            currentPackage.setDateExpired(newExpiredDate);
            if (terminate) {
                PackagePurchasedPaymentItemEntity payment = new PackagePurchasedPaymentItemEntity();
                payment.setPackagePurchased(currentPackage);
                payment.setAmountToTCube(cancelFee);
                String explanation = "Termination fee for terminating package " + currentPackage.getServicePackage().getName()
                        + " on " + date.getDate() + "/" + (date.getMonth() + 1) + "/" + (date.getYear() + 1900);
                payment.setExplanation(explanation);
                payment.setDateCreated(date);
                payment.setStakeholder(branch.getStakeholder());
                //set payment item to payment management
                entityManager.persist(payment);
                branch.getStakeholder().getPaymentItem().add(payment);
                entityManager.merge(branch.getStakeholder());
            }
        } else {
            throw new TerminateServicePackageException("You don't have any service package to terminate");
        }
    }

    public void changeCurrentPackage(long branchId, long selectedServicePackageId, int numOfMonth) throws ChangeServicePackageException {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);
        List<PackagePurchasedEntity> list = branch.getPackagePurchased();
        int pos = getCurrentPackage(list);
        String name = list.get(pos).getServicePackage().getName();
        ServicePackageEntity selectedServicePackage = entityManager.find(ServicePackageEntity.class, selectedServicePackageId);

        if (pos != - 1 && selectedServicePackage.equals(list.get(pos).getServicePackage())) {
            throw new ChangeServicePackageException("You must change to another service package");
        }


        try {
            terminateCurrentPackage(branchId, false);
        } catch (TerminateServicePackageException ex) {
        }

        Date beginDateOfNewPackage = new Date();
        if (pos != - 1) {
            if (list.get(pos).getServicePackage().getName().equalsIgnoreCase(FREE)) {
                branch.setBillingDay(beginDateOfNewPackage.getDate());
            } else {
                beginDateOfNewPackage = list.get(pos).getDateExpired();
            }
        }
        deleteRedundantPackagePurchased(list);

        if (name.equalsIgnoreCase(FREE)) {
            PackagePurchasedEntity temp = list.get(pos);
            branch.getPackagePurchased().remove(temp);
            entityManager.merge(branch);
            temp.getServicePackage().getPackagePurchased().remove(temp);
            entityManager.merge(temp.getServicePackage());
            entityManager.remove(temp);
        }

        buyNewPackage(branchId, beginDateOfNewPackage, selectedServicePackageId, numOfMonth);

        PackagePurchasedPaymentItemEntity payment = new PackagePurchasedPaymentItemEntity();

        if (!name.equalsIgnoreCase(TRIAL) && !name.equalsIgnoreCase(FREE)) {
            Date date = new Date();
            payment.setAmountToTCube(changeFee);
            String explanation = "Change fee for changing to package " + selectedServicePackage.getName()
                    + " on " + date.getDate() + "/" + (date.getMonth() + 1) + "/" + (date.getYear() + 1900);
            payment.setExplanation(explanation);
            payment.setDateCreated(date);
            payment.setStakeholder(branch.getStakeholder());
            //set payment item to payment management
            entityManager.persist(payment);
            branch.getStakeholder().getPaymentItem().add(payment);
            entityManager.merge(branch.getStakeholder());
        }
    }

    public PackagePurchasedEntity getCurrentPackagePurchased(Long branchId) {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);

        List<PackagePurchasedEntity> list = branch.getPackagePurchased();
        int pos = getCurrentPackage(list);

        if (pos != - 1) {
            return list.get(pos);
        }

        return null;
    }

    public void createNewBranch(StakeholderEntity selectedStakeholder, FnBBranchEntity selectedBranch, AddressEntity address, FnBIOTREntity fnBIOTR) {
        try {
            Date dateCreated = new Date();

            entityManager.persist(address);
            entityManager.persist(selectedBranch);
            entityManager.persist(fnBIOTR);

            selectedBranch.setBillingDay(dateCreated.getDate());
            selectedBranch.setStakeholder(selectedStakeholder);
            selectedBranch.setBranchAddress(address);

            selectedStakeholder.getBranches().add(selectedBranch);
            selectedBranch.setFnBIOTR(fnBIOTR);
            fnBIOTR.setFnBBranch(selectedBranch);
            entityManager.merge(selectedStakeholder);
            entityManager.flush();

            subscribeToTrialServicePackage(selectedBranch.getId());

        } catch (Exception ex) {
        }
    }

    private Date getExpiredDate(Date dateCreated, int numOfMonth) {
        int prevMonth = dateCreated.getMonth();
        int prevYear = dateCreated.getYear();
        int prevDate = dateCreated.getDate();
        int nextMonth, nextDate, nextYear;

        nextMonth = (prevMonth + numOfMonth) % 12;
        nextYear = prevYear + (prevMonth + numOfMonth) / 12;

        if (prevDate <= 28) {
            nextDate = prevDate;
        } else if (nextMonth == 1) {
            if (isLeapYear(nextYear)) {
                nextDate = 29;
            } else {
                nextDate = 28;
            }
        } else if (nextMonth == 3 || nextMonth == 5 || nextMonth == 9 || nextMonth == 10) {
            nextDate = Math.min(30, prevDate);
        } else {
            nextDate = Math.min(31, prevDate);
        }

        return new Date(nextYear, nextMonth, nextDate);
    }

    private boolean isLeapYear(int year) {
        if (year % 400 == 0) {
            return true;
        }
        if (year % 100 == 0) {
            return false;
        }
        if (year % 4 == 0) {
            return true;
        }
        return false;
    }

    public PackagePurchasedEntity getLatestPackagedPurchasedByBranchId(Long id) {
        BranchEntity branch = entityManager.find(BranchEntity.class, id);
        List<PackagePurchasedEntity> list = branch.getPackagePurchased();

        if (!list.isEmpty()) {
            return list.get(list.size() - 1);
        }
        return null;
    }

    public double getChangeFeeByBranchId(long branchId) {
        FnBBranchEntity branch = entityManager.find(FnBBranchEntity.class, branchId);

        return branch.getFnBIOTR().getChangeFee();
    }

    public void subscribeToFreeServicePackage(Long branchId) {
        Query query = entityManager.createQuery("SELECT s.id FROM ServicePackageEntity s WHERE s.name LIKE '%" + FREE + "%'");
        long id = (Long) query.getSingleResult();
        try {
            buyNewPackage(branchId, new Date(), id, MONTHOFTRIAL);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void subscribeToTrialServicePackage(Long branchId) {
        Query query = entityManager.createQuery("SELECT s.id FROM ServicePackageEntity s WHERE s.name LIKE '%" + TRIAL + "%'");
        long id = (Long) query.getSingleResult();
        try {
            buyNewPackage(branchId, new Date(), id, MONTHOFTRIAL);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public PackagePurchasedEntity getNextPackagePurchased(Long branchId) {
        BranchEntity branch = entityManager.find(BranchEntity.class, branchId);

        List<PackagePurchasedEntity> list = branch.getPackagePurchased();
        Date date = new Date();

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getDatePurchased().compareTo(date) > 0) {
                return list.get(i);
            }
        }

        return null;
    }
}
